package com.zhangdi.dwperms.config;

import com.zhangdi.dwperms.CustomModularRealmAuthenticator;
import com.zhangdi.dwperms.filter.RestAuthorizationFilter;
import com.zhangdi.dwperms.filter.RestFormAuthenticationFilter;
import com.zhangdi.dwperms.filter.RestShiroFilterFactoryBean;
import com.zhangdi.dwperms.matcher.JwtCredentialsMatcher;
import com.zhangdi.dwperms.realm.NormalRealm;
import com.zhangdi.dwperms.realm.JwtRealm;
import com.zhangdi.dwperms.service.impl.ShiroService;
import com.zhangdi.dwperms.token.AuthToken;
import com.zhangdi.dwperms.token.MSToken;
import net.sf.ehcache.CacheManager;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Configuration
public class ShiroConfig {
    @Bean
    public EhCacheManager ehCacheManager(CacheManager cacheManager) {
        EhCacheManager em = new EhCacheManager();
        em.setCacheManager(cacheManager);
        return em;
    }

    @Bean
    public NormalRealm normalRealm() {
        JwtCredentialsMatcher matcher = new JwtCredentialsMatcher();
        matcher.setHashAlgorithmName("SHA-1");
        matcher.setHashIterations(1024);
        NormalRealm realm = new NormalRealm();
        realm.setAuthenticationTokenClass(AuthToken.class);
        realm.setCredentialsMatcher(matcher);

        realm.setAuthorizationCachingEnabled(true);
        realm.setAuthorizationCacheName("cache_authorize");
        return realm;
    }

    @Bean
    public JwtRealm jwtRealm() {
        JwtRealm realm = new JwtRealm();
        realm.setAuthenticationTokenClass(AuthToken.class);

        realm.setAuthorizationCachingEnabled(true);
        realm.setAuthorizationCacheName("cache_authorize");
        return realm;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean restShiroFilterFactoryBean(SecurityManager securityManager) {
        RestShiroFilterFactoryBean shiroFilterFactoryBean = new RestShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        filters.put("authc", new RestFormAuthenticationFilter());
        filters.put("perms", new RestAuthorizationFilter());

        return shiroFilterFactoryBean;
    }

    @Bean(name = "securityManager")
    public SessionsSecurityManager securityManager(EhCacheManager ehCacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setCacheManager(ehCacheManager);

        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);

        return securityManager;
    }

    @EventListener(ApplicationStartedEvent.class)
    public void d(ApplicationStartedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        ShiroService shiroService = context.getBean(ShiroService.class);
        AbstractShiroFilter shiroFilter = context.getBean(AbstractShiroFilter.class);

        PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
                .getFilterChainResolver();
        DefaultFilterChainManager filterChainManager = (DefaultFilterChainManager) filterChainResolver
                .getFilterChainManager();

        Map<String, String> filterChainDefinitionMap = shiroService.getFromCache("sys");
        for (Map.Entry<String, String> entry : filterChainDefinitionMap.entrySet()) {
            String url = entry.getKey();
            String chainDefinition = entry.getValue().trim()
                    .replace(" ", "");
            filterChainManager.createChain(url, chainDefinition);
        }
    }

    @EventListener(ContextRefreshedEvent.class)
    public void handleContextRefresh(ContextRefreshedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) context.getBean("securityManager");

        NormalRealm normalRealm = context.getBean(NormalRealm.class);
        JwtRealm wxRealm = context.getBean(JwtRealm.class);
        List<Realm> realms = new ArrayList<>();
        realms.add(normalRealm);
        realms.add(wxRealm);
        securityManager.setRealms(realms);

        CustomModularRealmAuthenticator modularRealmAuthenticator = new CustomModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        modularRealmAuthenticator.setRealms(realms);
        securityManager.setAuthenticator(modularRealmAuthenticator);

        SecurityUtils.setSecurityManager(securityManager);
    }
}
